package com.lamatek.tags.google;

import java.io.Serializable;

import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;

import com.lamatek.tags.google.beans.GeocoderBean;
import com.lamatek.tags.google.beans.Ip2GeoCoder;

/**
 * GoogleMapPointTag
 * 
 * This class represents a &lt;googlemaps;point> tag. Developers should not subclass or override this
 * class or it's methods.
 * 
 * @author Tom Cole
 * @version 0.40
 */
public class GoogleMapPointTag extends TagSupport implements Serializable {
    
    String id = null;
    double latitude = -181.00d;
    double longitude = -181.00d;
    String ip = null;
    String address = null;
    String city = null;
    String state = null;
    String zipcode = null;
    String country = null;
    /**
     * Overrides doEndTag() from TagSupport. Developers should not override this method.
     */
    public int doEndTag() {
        if (longitude < -180 || latitude < -90 || longitude > 180 || latitude > 90) {
            if (geocode())
                addTag();
        }
        else {
	        addTag();
        }
        return EVAL_PAGE;
    }
    /**
     * Starts the geocoding process if the longitude and latitude either:
     * <ol>
     * <li>Have not been previously geocoded, or
     * <li>Have not been set.
     * </ol>
     * Automatically determines if this is an IP address or street address geocode,
     * and forwards to the appropriate geocoder.
     * 
     * @return True if geocoding resolved a longitude/latitude pair. False is not.
     */
    public boolean geocode() {
        if (longitude < -180 || latitude < -90 || longitude > 180 || latitude > 90) {
            if (ip == null) {
                GeocoderBean coder = new GeocoderBean();
                coder.setAddress(address);
                coder.setCity(city);
                coder.setState(state);
                coder.setCountry(country);
                coder.setZip(zipcode);
                if (coder.geocode()) {
                    longitude = coder.getLongitude();
                    latitude = coder.getLatitude();
                    return true;
                }
                else {
                    return false;
                }
            }
            else {
                Ip2GeoCoder coder = new Ip2GeoCoder();
                coder.setIp(ip);
                if (coder.geocode()) {
                    longitude = coder.getLongitude();
                    latitude = coder.getLatitude();
                    return true;
                }
                else {
                    return false;
                }
            }
        }
        else {
            return true;
        }
    }
    /**
     * Overrides doStartTag() from TagSupport. Developers should not override this method.
     */
    public int doStartTag(){
        return SKIP_BODY;
    }
    /**
     * Adds this tag to the parent GoogleMapTag.
     */
    private void addTag() {
        Tag tag = this;
        while (tag.getParent() != null) {
            if (tag.getParent() instanceof GoogleMapTag) {
                ((GoogleMapTag) tag.getParent()).addPoint(this);
                return;
            }
            tag = tag.getParent();
        }
    }
    /**
     * Sets this point as the initial centerpoint of the entire map.
     * 
     * @param center True if the map should be centered around this point. False if not.
     */
    public void setCenter(boolean center) {
        if (center) {
            Tag tag = this;
            while (tag.getParent() != null)
                if (tag.getParent() instanceof GoogleMapTag) {
                    ((GoogleMapTag) tag.getParent()).setCenterLatitude(latitude);
                    ((GoogleMapTag) tag.getParent()).setCenterLongitude(longitude);
                    return;
                }
        }
    }
    /**
     * Sets the latitude (in decimal form) for this point. If using IP or street addresses,
     * this is called automatically by the resulting Geocoder.
     * 
     * @param latitude The latitude (in decimal form)
     */
    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }
    /**
     * Sets the longitude (in decimal form) for this point. If using IP or street addresses,
     * this is called automatically by the resulting Geocoder.
     * 
     * @param longitude The longitude (in decimal form)
     */
    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }
    /**
     * Attempts to set the latitude using a String, by converting it to a double.
     * 
     * @param value A valid latitude, specified as a String.
     */
    public void setLatitudeAsString(String value) {
        try {
           latitude = Double.parseDouble(value); 
        }
        catch(NumberFormatException nfe) {
           nfe.printStackTrace(System.err);
        }
    }
    /**
     * Attempts to set the longitude using a String, by converting it to a double.
     * 
     * @param value A valid longitude, specified as a String.
     */
    public void setLongitudeAsString(String value) {
        try {
           longitude = Double.parseDouble(value); 
        }
        catch(NumberFormatException nfe) {
           nfe.printStackTrace(System.err);
        }
    }
    /**
     * Returns the address used when creating this marker, or null if this point was
     * created using an IP or longitude/latitude pair.
     * 
     * @return Address string or null.
     */
    public String getAddress() {
        return address;
    }
    /**
     * Sets the street address used to geocode the longitude/latitude.
     * 
     * @param address A valid street address (i.e. 74 Connor Lane).
     */
    public void setAddress(String address) {
        this.address = address;
    }
    /**
     * Returns the city used when creating this marker, or null if this point was
     * created using an IP or longitude/latitude pair.
     * 
     * @return Us or Intl. city string or null.
     */
    public String getCity() {
        return city;
    }
    /**
     * Sets the city name used to geocode the longitude/latitude. This can
     * be US or international.
     * 
     * @param city A valid city name.
     */
    public void setCity(String city) {
        this.city = city;
    }
    /**
     * Returns the ISO compliant 2-digit country code used when creating this marker, or null if this point was
     * created using an IP or longitude/latitude pair.
     * 
     * @return Country code as a string or null.
     */
    public String getCountry() {
        return country;
    }
    /**
     * Sets the ISO compliant 2-character country code used to geocode the longitude/latitude.
     * 
     * @param country A valid 2-character country code.
     */
    public void setCountry(String country) {
        this.country = country;
    }
    /**
     * Returns the IP address used when creating this marker, or null if this point was
     * created using a street address or longitude/latitude pair.
     * 
     * @return The IPv4 or IPv6 address as a string or null.
     */
    public String getIp() {
        return ip;
    }
    /**
     * Sets the IP address used to geocode the longitude/latitude.
     * 
     * @param ip A valid ip address (i.e. 216.113.235.52).
     */
    public void setIp(String ip) {
        this.ip = ip;
    }
    /**
     * Returns the state or province used when creating this marker, or null if this point was
     * created using an IP or longitude/latitude pair.
     * 
     * @return State or province string or null.
     */
    public String getState() {
        return state;
    }
    /**
     * Sets the state or province used to geocode the longitude/latitude.
     * 
     * @param state A valid state or province.
     */
    public void setState(String state) {
        this.state = state;
    }
    /**
     * Returns the zip or postal code used when creating this marker, or null if this point was
     * created using an IP or longitude/latitude pair.
     * 
     * @return Zipcode or postal code as a string or null.
     */
    public String getZipcode() {
        return zipcode;
    }
    /**
     * Sets the zip or postal used to geocode the longitude/latitude.
     * 
     * @param zipcode A valid US ZIP or international postal code.
     */
    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }
    /**
     * Returns the latitude (in decimal form) for this point. This may have been 
     * set programmatically, or if using addresses, may have been geocoded after
     * calling the geocode() method. If using addresses, developers should be sure to
     * call geocode() before calling this method.
     * 
     * @return The latitude of this point or -181 if non-codable.
     */
    public double getLatitude() {
        return latitude;
    }
    /**
     * Returns the longitude (in decimal form) for this point. This may have been 
     * set programmatically, or if using addresses, may have been geocoded after
     * calling the geocode() method. If using addresses, developers should be sure to
     * call geocode() before calling this method.
     * 
     * @return The longitude of this point or -181 if non-codable.
     */
    public double getLongitude() {
        return longitude;
    }
    /**
     * Returns the unique id for this point.
     * 
     * @return The points id.
     */
    public String getId() {
        return id;
    }
    /**
     * Sets the unique id for this point. Other overlays will use this id
     * to reference this point in their construction.
     * 
     * @param id A unique id for this point.
     */
    public void setId(String id) {
        this.id = id;
    }
}
